package executors;

import il.technion.ewolf.kbr.KeybasedRouting;
import il.technion.ewolf.kbr.Node;
import il.technion.ewolf.kbr.openkad.KadNetModule;

import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;

import com.google.inject.Guice;
import com.google.inject.Injector;

import tweeter.LogParser;
import tweeter.NetworkEntity;
import tweeter.Tweet;
import quasar.Station;
import quasar_framework.Common;

public class TweeterFeedQuasarExecutor
{
	@SuppressWarnings("static-access")
	public static void main(String[] args) throws Exception
	{
		Random generator = new Random();
		
		Map<Integer, NetworkEntity> entities = new HashMap<Integer, NetworkEntity>();
		Set<Integer> entityIDs = LogParser.generateAllEntityIDs("feeds/followers/9", "feeds/tweets/9");
		Map<Integer, Integer> indexToIDMapping = new HashMap<Integer, Integer>();
		Iterator<Integer> entityIterator = entityIDs.iterator();
		int portID = 0;
		System.out.println("Creating the entities...");
		while (entityIterator.hasNext())
		{
			Integer ID = entityIterator.next();
			Injector injector = Guice.createInjector(new KadNetModule()
			.setProperty("openkad.keyfactory.keysize", "4")
			.setProperty("openkad.bucket.kbuckets.maxsize", "4")
			.setProperty("openkad.executors.server.nrthreads", "2")
			.setProperty("openkad.executors.client.nrthreads", "1")
			.setProperty("openkad.executors.ping.nrthreads", "1")
			.setProperty("openkad.executors.client.max_pending", "128")
			.setProperty("openkad.executors.server.max_pending", "128")
			.setProperty("openkad.executors.ping.max_pending", "16")
			.setProperty("openkad.seed", ""+(portID+Common.basePort))
			.setProperty("openkad.net.udp.port", ""+(portID+Common.basePort)));
	        KeybasedRouting kbr = injector.getInstance(KeybasedRouting.class);
	        kbr.create();
			NetworkEntity entity = (NetworkEntity) new Station(ID, kbr);
			entities.put(ID, entity);
			indexToIDMapping.put(portID, ID);
			portID++;
		}
		
		System.out.println("Group joining...");
		for (int index = 0 ; index < entityIDs.size() ; index++)
		{
			int entityToJoin = generator.nextInt(entityIDs.size());
			while (index == entityToJoin)
			{
				entityToJoin = generator.nextInt(entityIDs.size());
			}
			
			((Station)entities.get(indexToIDMapping.get(entityToJoin))).kbr.join(Arrays.asList(new URI("openkad.udp://127.0.0.1:"+(Common.basePort + index)+"/")));
		}
		
		System.out.println("Creating a P2P web...");
		for (int index = 0 ; index < entityIDs.size() ; index++)
		{
			List<Node> kbrNeighbours = ((Station)entities.get(indexToIDMapping.get(index))).kbr.getNeighbours();
			for (Node neighbour : kbrNeighbours)
			{
				((Station)entities.get(indexToIDMapping.get(index))).kbr.sendMessage(neighbour, "HI", null);
			}
		}
		
		System.out.println("Sleeping for 30 seconds...");
		Thread.currentThread().sleep(30000);
		
		System.out.println("Subscribing...");
		Map<Integer, HashSet<Integer>> subscriptions = LogParser.generateSubscriptions("feeds/followers/9");

		int runningCounter = 0;
		for (Integer ID : subscriptions.keySet())
		{
			NetworkEntity entity = entities.get(ID);
			((Station)entity).subscriptions = (HashSet<Integer>) subscriptions.get(ID); // TODO: For now
			entity.subscribe(runningCounter++);
		}
		
		System.out.println("Sleeping for 3 minutes...");
		Thread.currentThread().sleep(180000);
		
		
		ArrayList<Tweet> tweets = LogParser.generateTweets("feeds/tweets/9");

//		int startingPoint = generator.nextInt(tweets.size());
//		System.out.println(startingPoint);
//		while (((tweets.size() - startingPoint) < 5000) || (5000 > startingPoint))
//		{
//			startingPoint = generator.nextInt(tweets.size());
//			System.out.println(startingPoint);
//		}
//		
//		Iterator<Tweet> tweetIterator = tweets.iterator();
//		int iteratorCounter = 0;
//		Tweet startingTweet = null;
//		while (tweetIterator.hasNext() && iteratorCounter < startingPoint)
//		{
//			startingTweet = tweetIterator.next();
//			iteratorCounter++;
//		}
//		
//		ArrayList<Tweet> selectedTweets = new ArrayList<Tweet>();
//		selectedTweets.add(startingTweet);
//		
//		Calendar upperBound = (Calendar) startingTweet.tweetTimestamp.clone();
//		upperBound.add(Calendar.HOUR, 2);
//		Tweet runningTweet = tweetIterator.next();
//		while (tweetIterator.hasNext() && runningTweet.tweetTimestamp.before(upperBound))
//		{
//			selectedTweets.add(runningTweet);
//			runningTweet = tweetIterator.next();
//		}
		
		Iterator<Tweet> tweetIterator = tweets.iterator();
		ArrayList<Tweet> selectedTweets = new ArrayList<Tweet>();
		while (tweetIterator.hasNext())
		{
			selectedTweets.add(tweetIterator.next());
		}
		System.out.println("Tweeting...");
		Tweet runningTweet = selectedTweets.get(0);
		entities.get(runningTweet.tweeterID).publish(0, runningTweet.tweetData);
		Calendar previoustTweetTime = null;
		for (int index = 1 ; index < selectedTweets.size() ; index++)
		{
			previoustTweetTime = runningTweet.tweetTimestamp;
			runningTweet = selectedTweets.get(index);
//			Thread.sleep (runningTweet.tweetTimestamp.getTimeInMillis() - previoustTweetTime.getTimeInMillis());
			Thread.sleep(3000);
			entities.get(runningTweet.tweeterID).publish(index, runningTweet.tweetData);
		}
		
		System.out.println("Sleeping for 5 minutes...");
		Thread.currentThread().sleep(300000);
		
	}
}
